package cn.microservices.cloud.common.web.aops;


import cn.microservices.cloud.common.core.annotation.AuditLogger;
import cn.microservices.cloud.common.core.constant.LoginConstant;
import cn.microservices.cloud.common.core.entity.AuditLog;
import cn.microservices.cloud.common.core.exceptions.BusinessException;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

/**
 * 自动日志输出 aop
 *
 * @author HYF
 * @since 1.0.0
 */
@Aspect
@Component
@EnableAspectJAutoProxy
public class AutoLogAop {

    private static final Logger LOG = LoggerFactory.getLogger(AutoLogAop.class);
    @Autowired
    @Qualifier("tokenRedis")
    private RedisTemplate redisTemplate;

    /**
     * 执行核心方法
     * <p>
     * 相当于 MethodInterceptor
     *
     * @param point   切点
     * @param autoLog 日志参数
     * @return 结果
     * @throws Throwable 异常信息
     * @since 0.0.3
     */
    @Around("@annotation(autoLog)")
    public Object around(ProceedingJoinPoint point, AuditLogger autoLog) throws Throwable {
        Method method = getCurrentMethod(point);
        //方法名
        String methodName = method.getName();
        AuditLog auditLog = new AuditLog();

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = requestAttributes.getRequest();
            String ip = request.getRemoteAddr();
            auditLog.setIp(ip);
            String identityId = request.getHeader("userId");
            String identityType = request.getHeader("identity");
            String platform = request.getHeader("equipmentType");
            String token = request.getHeader("token");
            String prefix = request.getHeader("prefix");
            auditLog.setType(prefix);
            auditLog.setIdentityId(identityId);
            auditLog.setIdentityType(identityType);
            auditLog.setPlatform(platform);
            Object loginUserObj = redisTemplate.opsForValue().get(LoginConstant.USER_TOKEN_LOGIN_INFO_KEY + token);
            Object accountObj = redisTemplate.opsForValue().get(LoginConstant.USER_TOKEN_ACCOUNT_KEY + token);
            if (!Objects.isNull(loginUserObj)) {
                JSONObject jsonObject = JSONUtil.parseObj(loginUserObj);
                auditLog.setUserName(jsonObject.getStr("name"));
            }
            if ("EMP".equals(identityType) && !Objects.isNull(loginUserObj)) {
                JSONObject jsonObject = JSONUtil.parseObj(loginUserObj);
                auditLog.setAccountName(jsonObject.getStr("name"));
            } else {
                if (!Objects.isNull(accountObj)) {
                    JSONObject jsonObject = JSONUtil.parseObj(accountObj);
                    auditLog.setAccountName(jsonObject.getStr("username"));
                }
            }
            LocalDateTime time = LocalDateTime.now();
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String dateTime = dtf.format(time);
            auditLog.setCurrentTime(dateTime);
            auditLog.setMethodName(methodName);
            AuditLogger alm = method
                    .getAnnotation(AuditLogger.class);
            if (!Objects.isNull(alm)&&StringUtils.isNotBlank(alm.name())) {
                auditLog.setName(alm.name());
            }
        }

        try {
            if (point.getArgs().length>0){
                auditLog.setParams(JSONUtil.parseObj(point.getArgs()[0], true, true));

            }
            //2. 执行方法
            Object result = point.proceed();
            addAuditLog(auditLog);
            return result;
        } catch (Throwable e) {
            LOG.error("{} meet ex.", methodName, e);
            throw e;
        }
    }

    /**
     * 获取当前方法信息
     *
     * @param point 切点
     * @return 方法
     */
    private Method getCurrentMethod(ProceedingJoinPoint point) {
        try {
            Signature sig = point.getSignature();
            MethodSignature msig = (MethodSignature) sig;
            Object target = point.getTarget();
            return target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        } catch (NoSuchMethodException e) {
            throw new BusinessException(e.getMessage());
        }
    }

    /**
     * 日志持久话 直接打印 ELK采集日志
     *
     * @param auditLog
     */

    private void addAuditLog(AuditLog auditLog) {

        LOG.info("埋点日志打印 logData={}", JSONUtil.parseObj(auditLog, true, true));
    }
}


